package com.cloudbroker.bcs.platform.ltb.middle.mybatis;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

import javax.xml.bind.PropertyException;

import com.cloudbroker.bcs.common.entity.Markable;
import com.cloudbroker.bcs.common.util.ReflectionUtil;
import com.cloudbroker.bcs.platform.ltb.bizapi.service.base.vo.BrokerVO;
import com.cloudbroker.bcs.platform.ltb.bizapi.service.base.vo.HSLTBBaseVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cloudbroker.bcs.platform.ltb.middle.mybatis.dialect.Dialect;


/**
 * Created by zhaosh19213 on 2016/8/18.
 */

@Intercepts({ @Signature(type = Executor.class, method = "query", args = {
        MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })
public class ShardInterceptor implements Interceptor, Serializable {

    /**
     * serialVersionUID.
     */
    private static final long serialVersionUID = -4420829689661146956L;

    /**
     * Logger.
     */
    private static Logger logger = LoggerFactory.getLogger(PaginationInterceptor.class);

    /**
     * 数据库方言.
     */
    protected Dialect dialect;

    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        if (ShardContext.isExistShardContext()) {
            Configuration configuration = mappedStatement.getConfiguration();
            Object parameter = invocation.getArgs()[1];
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            String originalSql = boundSql.getSql();
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();

            Object parameterObject = boundSql.getParameterObject();

            //如果多表查询 或者 参数为列表 则不执行
            if(StringUtils.contains(boundSql.getSql()," join ") ||
                    parameterObject.getClass().getName().matches("org.apache.ibatis.session.defaults.*")||
                    parameterObject.getClass().getName().matches("java.util.*")) {
                return invocation.proceed();
            }

            ShardContext context = ShardContext.getShardContext();

            //对象继承HSLTBBaseVO是否写入company_id
            if(!parameterObject.getClass().getName().matches("java.lang.*")&&
                    (parameterObject instanceof Markable || parameterObject instanceof HSLTBBaseVO ) ) {

                Method method = parameterObject.getClass().getMethod("setCompany_id", String.class);
                if (method!=null){
                    method.invoke(parameterObject.getClass().newInstance(), context.getCompany_id());
                }
                boolean isExistCompany_id = false;
                for (ParameterMapping temp :parameterMappingList) {
                    if (temp.getProperty().equals("company_id")) {
                        isExistCompany_id = true;
                    }
                }
                if (parameterMappingList !=null && parameterMappingList.size()>0 &&
                        isExistCompany_id==false){
                    //不存在company_id条件则增加company_id
//                    String shardSql = originalSql.replace("where", "where company_id="+context.getCompany_id() +" and ");
                    String shardSql = originalSql.replaceAll("(?i)where", "WHERE company_id="+context.getCompany_id() +" and ");
                    BoundSql newBoundSql = new BoundSql(
                            mappedStatement.getConfiguration(), shardSql,
                            parameterMappingList,
                            parameterObject);
                    MappedStatement newMs = copyFromMappedStatement(
                            mappedStatement, new PaginationInterceptor.BoundSqlSqlSource(newBoundSql));
                    invocation.getArgs()[0] = newMs;
                    invocation.getArgs()[1] = parameterObject;
                }
            }else {

                List<ParameterMapping> replaceParameterMapping = new ArrayList<>();
                ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, "company_id", String.class);
                replaceParameterMapping.add(builder.build());
                replaceParameterMapping.addAll(parameterMappingList);


                Map<String, Object> newParameterObject = new HashMap<>();
                newParameterObject.put("company_id", context.getCompany_id());
                if (parameterMappingList!=null && parameterMappingList.size()>0) {
                    newParameterObject.put(parameterMappingList.get(0).getProperty(), parameterObject);
                    String shardSql = originalSql.replaceAll("(?i)where", "WHERE company_id=? and ");
                    BoundSql newBoundSql = new BoundSql(
                            mappedStatement.getConfiguration(), shardSql,
                            replaceParameterMapping,
                            newParameterObject);
                    MappedStatement newMs = copyFromMappedStatement(
                            mappedStatement, new PaginationInterceptor.BoundSqlSqlSource(newBoundSql));

                    invocation.getArgs()[0] = newMs;
                    invocation.getArgs()[1] = newParameterObject;
                }
            }
            return invocation.proceed();
        }else {
            return invocation.proceed();
        }
    }

    public Object plugin(Object arg0) {
        return Plugin.wrap(arg0, this);
    }

    public void setProperties(Properties properties) {
    }

    /**
     * copyFromMappedStatement.
     */
    private MappedStatement copyFromMappedStatement(MappedStatement ms,
                                                    SqlSource newSqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(
                ms.getConfiguration(), ms.getId(), newSqlSource,
                ms.getSqlCommandType());
        if (ms.getKeyProperties() != null) {
            for (String keyProperty : ms.getKeyProperties()) {
                builder.keyProperty(keyProperty);
            }
        }
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.cache(ms.getCache());
        return builder.build();
    }
    public static void main(String[] args) {
        String p = "select * from xx WHERE ";
        System.out.println(p.replaceAll("(?i)where","where haha"));

    }
}
